package com.ugold.common.util.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;

import com.alibaba.fastjson.JSONObject;
import com.ugold.common.util.validate.JSR303Util;
import com.ugold.common.util.validate.ValidateResult;
import com.ugold.compoment.annoation.InParamsNotOutput;
import com.ugold.compoment.annoation.OutParamsNotOutput;
import com.ugold.compoment.exception.BizException;
import com.ugold.compoment.exception.ErrorEnum;
import com.ugold.compoment.exception.IntegrationException;
import com.ugold.compoment.model.BaseReq;
import com.ugold.compoment.model.Result;

import lombok.extern.slf4j.Slf4j;

/**
 * 服务请求切面基类
 * 
 * @author dingjian
 *
 */
// @Aspect
// @Component
@Slf4j
public class BaseFacadeAspect {
	// @Pointcut("execution(* com.jcgroup.xxx.yyy.service.facade.*.*(..))")
	// private void allMethod() {
	// }

	// @Around("allMethod()")
	public Object doFacadeAround(ProceedingJoinPoint call) throws Throwable {
		MethodSignature signature = (MethodSignature) call.getSignature();
		Method method = signature.getMethod();
		String fullMethodName = String.format("%s.%s", method.getDeclaringClass().getName(), method.getName());
		Object[] args = call.getArgs();

		if (!hasNoAspectAnnotation(method, InParamsNotOutput.class)) {
			log.info("[SERVICE_REQUEST]{},Params={}", fullMethodName, JSONObject.toJSONString(args));
		} else {
			log.info("[SERVICE_REQUEST]{},Params=定义为不输出", fullMethodName);
		}

		Long startTime = System.currentTimeMillis();
		Result<?> baseRes = new Result<>();
		try {
			Object target = call.getTarget();
			ValidateResult validateResult = JSR303Util.validateFacadeParamters(target, method, args);
			if (validateResult != null && !validateResult.isPass()) {
				Result<Object> wrapErrorResult = Result.buildFailureResult(ErrorEnum.ERROR_PARAM.getErrorCode(),
						validateResult.getMessagesAsString(";"));
				return wrapErrorResult;
			}
			baseRes = (Result<?>) call.proceed();
			return baseRes;
		} catch (IntegrationException e) {
			baseRes.setCode(e.getErrorCode());
			baseRes.setMessage(e.getErrorMessage());
			log.warn("IntegrationException异常，异常信息："
					+ (StringUtils.isNotEmpty(e.getErrorMessage()) ? e.getErrorMessage() : e.getMessage()), e);
			return baseRes;
		} catch (BizException e) {
			baseRes.setCode(e.getErrorCode());
			baseRes.setMessage(e.getErrorMessage());
			log.warn("业务BizException异常，异常信息："
					+ (StringUtils.isNotEmpty(e.getErrorMessage()) ? e.getErrorMessage() : e.getMessage()), e);
			return baseRes;
		} catch (Exception e) {
			baseRes.setCode(ErrorEnum.ERROR_DEFAULT.getErrorCode());
			baseRes.setMessage(ErrorEnum.ERROR_DEFAULT.getErrorMessage());
			log.error("未知Exception异常，异常信息:" + e.getMessage(), e);
			return baseRes;
		} finally {
			if (!hasNoAspectAnnotation(method, OutParamsNotOutput.class)) {
				log.info("[SERVICE_RESPONSE]{},Result={}", fullMethodName, JSONObject.toJSONString(baseRes));
			} else {
				log.info("[SERVICE_RESPONSE]{},Result=定义为不输出", fullMethodName);
			}
			long runTimes = System.currentTimeMillis() - startTime;
			log.info("{},Code={},Time Consuming={}ms", fullMethodName, baseRes.getCode(), runTimes);
			MDC.clear();
		}
	}

	/**
	 * 处理Controller切面
	 * 
	 * @param call
	 * @return
	 * @throws Throwable
	 */
	public Object doControllerAround(ProceedingJoinPoint call) throws Throwable {
		MethodSignature signature = (MethodSignature) call.getSignature();
		Method method = signature.getMethod();
		String fullMethodName = String.format("%s.%s", method.getDeclaringClass().getName(), method.getName());
		Object[] args = call.getArgs();

		Long startTime = System.currentTimeMillis();
		Result<?> baseRes = new Result<>();
		try {
			// 入参数合法性校验
			for (int i = 0; i < args.length; i++) {
				if (args[i] instanceof BaseReq) {
					BaseReq tmpBase = (BaseReq) args[i];
					log.info("[SERVICE_REQUEST]{},Params[{}]={}", fullMethodName, i, JSONObject.toJSONString(tmpBase));
					// 入参校验
					ValidateResult checkResult = JSR303Util.validate(tmpBase, false);
					if (checkResult != null && !checkResult.isPass()) {
						Result<?> jsonResult = Result.buildFailureResult(ErrorEnum.ERROR_PARAM.getErrorCode(),
								checkResult.getMessagesAsString(";"));
						log.info("请求入参不合法，返回结果{}", JSONObject.toJSONString(jsonResult));
						MDC.clear();
						return jsonResult;
					}
					// 特殊业务校验
					tmpBase.checkData();
				}
			}
			baseRes = (Result<?>) call.proceed();
			return baseRes;
		} catch (IntegrationException e) {
			baseRes.setCode(e.getErrorCode());
			baseRes.setMessage(e.getErrorMessage());
			log.warn("IntegrationException异常，异常信息："
					+ (StringUtils.isNotEmpty(e.getErrorMessage()) ? e.getErrorMessage() : e.getMessage()), e);
			return baseRes;
		} catch (BizException e) {
			baseRes.setCode(e.getErrorCode());
			baseRes.setMessage(e.getErrorMessage());
			log.warn("业务BizException异常，异常信息："
					+ (StringUtils.isNotEmpty(e.getErrorMessage()) ? e.getErrorMessage() : e.getMessage()), e);
			return baseRes;
		} catch (Exception e) {
			baseRes.setCode(ErrorEnum.ERROR_DEFAULT.getErrorCode());
			baseRes.setMessage(ErrorEnum.ERROR_DEFAULT.getErrorMessage());
			log.error("未知Exception异常，异常信息：" + e.getMessage(), e);
			return baseRes;
		} finally {
			if (!hasNoAspectAnnotation(method, OutParamsNotOutput.class)) {
				log.info("[SERVICE_RESPONSE]{},Result={}", fullMethodName, JSONObject.toJSONString(baseRes));
			} else {
				log.info("[SERVICE_RESPONSE]{},Result=定义为不输出", fullMethodName);
			}
			long runTimes = System.currentTimeMillis() - startTime;
			log.info("{},Code={},Time Consuming={}ms", fullMethodName, baseRes.getCode(), runTimes);
			MDC.clear();
		}
	}

	/**
	 * 判断是否存在某个指定注解
	 * 
	 * @author dingjian
	 * @date 2019/04/24 10:42:39
	 * @param method
	 * @param anno
	 * @return
	 */
	private boolean hasNoAspectAnnotation(Method method, Class anno) {
		Annotation annotation = method.getAnnotation(anno);
		if (annotation != null) {
			return true;
		}
		return false;
	}
}